package cn.haigui.icrawler.downloader.selenium;

import cn.haigui.icrawler.core.downloader.IDownloader;
import com.google.common.base.Function;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.time.Duration;
import java.util.Objects;

@Slf4j
public class SeleniumDownloader implements IDownloader {
    @Getter
    WebDriver webDriver;
    @Getter
    ChromeOptions options=new ChromeOptions();
    String exePath="\"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\"";
    private Process process;
    private int port;

    public SeleniumDownloader() {
//        options.setCapability("pageLoadStrategy", "eager");
//        #option.set_capability("detach", True)
//        #option.set_capability("--enable-automation", False)
//        options.addArguments("--disable-blink-features=AutomationControlled");
        init();
    }

    public SeleniumDownloader(ChromeOptions options) {
        this.options = options;
        init();
    }
    private void init(){
        if (webDriver == null) {
            try {
                port = findAvailablePort();
                String profilePath = new File("").getAbsolutePath();
                String threadName = Thread.currentThread().getName();
                // 不同的profile chrome才会起多个websocket，才能多线程操作chrome
                String cmd = String.format("%s --remote-debugging-port=%s --user-data-dir=%s/profile/%s --remote-allow-origins=* "
                        , exePath, port, profilePath,threadName);
                process = Runtime.getRuntime().exec(cmd);
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
            options.setExperimentalOption("debuggerAddress", "127.0.0.1:" + port);
            webDriver = new ChromeDriver(options);
        }
        log.info("[seleniumDownloader]使用端口:{}",port);
    }

    @Override
    public WebDriver get(String url) {
        webDriver.get(url);
        return webDriver;
    }

    @Override
    public String getCurrentUrl() {
        return webDriver.getCurrentUrl();
    }

    @Override
    public String getPageSource() {
        return webDriver.getPageSource();
    }

    public boolean waitLoad() {
        return waitLoad(30);
    }

    public WebDriver switchTo(String windowHandle) {
        webDriver = webDriver.switchTo().window(windowHandle);
        return webDriver;
    }

    /**
     * 超时返回FALSE
     *
     * @param seconds
     * @return
     */
    public boolean waitLoad(int seconds) {
        // 等待
        WebDriverWait wait = new WebDriverWait(webDriver, Duration.ofSeconds(seconds));
        try {
            wait.until((Function<WebDriver, Object>) webDriver ->
                    Objects.equals("complete", ((JavascriptExecutor) webDriver)
                            .executeScript("return document.readyState")));
            return true;
        } catch (TimeoutException e) {
            log.error("加载超时.[{}]", webDriver.getCurrentUrl());
            return false;
        }
    }

    /**
     * // 滚动到底部
     *
     * @return
     */
    public boolean scrollToBottom() {
        try {
            ((JavascriptExecutor) webDriver).executeScript("window.scrollTo(0, document.body.scrollHeight)");
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public void quit() {
        if (webDriver != null) {
            webDriver.quit();
            process.destroy();
        }
    }

    public static int findAvailablePort() {
        try {
            ServerSocket socket = new ServerSocket(0);
            int localPort = socket.getLocalPort();
            socket.close();
            return localPort;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("无法获取端口号", e);
        }
    }
}
